package com.zemo;

import com.google.common.collect.Maps;
import com.zemo.util.Digests;
import com.zemo.util.RSA;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.util.*;

/**
 * @ClassName: AlipayUtil
 * @author: SuperZemo
 * @email: zemochen@gmail.com
 * @Date 12/30/15 10:36
 * @Description 支付宝支付相关工具
 */
public class AlipayUtil {
    // 签名方式 不需修改
    public static String sign_type = "RSA";
    // 字符编码格式 目前支持 gbk 或 utf-8
    public static String input_charset = "utf-8";
    private static final String ALIPAY_NOTIFY_URL = "https://mapi.alipay.com/gateway.do?service=%s&partner=%s&notify_id=%s";
    private static final String PARENT_ID = "";
    private static final String NOTIFY_URL = "http://test.lanbaoo.com/growth/alipay/async";
    private static final String RETURN_URL = "http://test.lanbaoo.com/growth/alipay/sync";
    private static final String PUBLIC_KEY = "";
    private static final String RSA_PRIVATE = "";


    public static Map<String, String> buildRequestPara(Map<String, String> map, String key) {
        // 除去数组中的空值和签名参数
        Map<String, String> sPara = paraFilter(map);
        // 生成签名结果
        String mysign = buildRequestMysign(sPara, key);

        // 签名结果与签名方式加入请求提交参数组中
        sPara.put("sign", mysign);
        sPara.put("sign_type", "MD5");

        return sPara;
    }

    public static Map<String, String> paraFilter(Map<String, String> map) {
        Map<String, String> result = new HashMap<String, String>();

        if (map == null || map.size() <= 0) {
            return result;
        }

        for (String key : map.keySet()) {
            String value = map.get(key);
            if (value == null || value.equals("")
                    || key.equalsIgnoreCase("sign")
                    || key.equalsIgnoreCase("sign_type")) {
                continue;
            }
            result.put(key, value);
        }
        return result;
    }

    /**
     * 验证消息是否是支付宝发出的合法消息
     *
     * @param params 通知返回来的参数数组
     * @return 验证结果
     */
    public static boolean verify(Map<String, String> params) {

        //判断responsetTxt是否为true，isSign是否为true
        //responsetTxt的结果不是true，与服务器设置问题、合作身份者ID、notify_id一分钟失效有关
        //isSign不是true，与安全校验码、请求时的参数格式（如：带自定义参数等）、编码格式有关
        String responseTxt = "true";
        if (params.get("notify_id") != null) {
            String notify_id = params.get("notify_id");
            responseTxt = verifyResponse(notify_id);
        }
        String sign = "";
        if (params.get("sign") != null) {
            sign = params.get("sign");
        }
        boolean isSign = getSignVeryfy(params, sign);

        //写日志记录（若要调试，请取消下面两行注释）
        String sWord = "responseTxt=" + responseTxt + "\n isSign=" + isSign + "\n 返回回来的参数：" + createLinkString(params);

//        System.out.println(sWord);
        //AlipayCore.logResult(sWord);

        if (responseTxt.equals("true")) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * 根据反馈回来的信息，生成签名结果
     *
     * @param Params 通知返回来的参数数组
     * @param sign   比对的签名结果
     * @return 生成的签名结果
     */
    private static boolean getSignVeryfy(Map<String, String> Params, String sign) {
        //过滤空值、sign与sign_type参数
        Map<String, String> sParaNew = paraFilter(Params);
        //获取待签名字符串
        String preSignStr = createLinkString(sParaNew);

        //获得签名验证结果
        boolean isSign = false;
        if (sign_type.equals("RSA")) {
            isSign = RSA.verify(preSignStr, sign, PUBLIC_KEY, input_charset);
        }
        return isSign;
    }

    private static String buildRequestMysign(Map<String, String> sPara,
                                             String key) {
        String prestr = createLinkString(sPara); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
        String mysign = Digests.MD5(prestr + key);
        return mysign;
    }

    public static String createLinkString(Map<String, String> map) {
        List<String> keys = new ArrayList<String>(map.keySet());
        Collections.sort(keys);

        String prestr = "";

        for (int i = 0; i < keys.size(); i++) {
            String key = keys.get(i);
            String value = map.get(key).toString();

            if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
                prestr = prestr + key + "=" + "\"" + value + "\"";
            } else {
                prestr = prestr + key + "=" + "\"" + value + "\"" + "&";
            }
        }

        return prestr;
    }

    /**
     * 获取远程服务器ATN结果,验证返回URL
     *
     * @param notify_id 通知校验ID
     * @return 服务器ATN结果
     * 验证结果集：
     * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空
     * true 返回正确信息
     * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    private static String verifyResponse(String notify_id) {
        //获取远程服务器ATN结果，验证是否是支付宝服务器发来的请求

        String veryfy_url = String.format(ALIPAY_NOTIFY_URL, "notify_verify", PARENT_ID, notify_id);

        return checkUrl(veryfy_url);
    }

    /**
     * 获取远程服务器ATN结果
     *
     * @param urlvalue 指定URL路径地址
     * @return 服务器ATN结果
     * 验证结果集：
     * invalid命令参数不对 出现这个错误，请检测返回处理中partner和key是否为空
     * true 返回正确信息
     * false 请检查防火墙或者是服务器阻止端口问题以及验证时间是否超过一分钟
     */
    private static String checkUrl(String urlvalue) {
        String inputLine = "";

        try {
            URL url = new URL(urlvalue);
            HttpURLConnection urlConnection = (HttpURLConnection) url.openConnection();
            BufferedReader in = new BufferedReader(new InputStreamReader(urlConnection
                    .getInputStream()));
            inputLine = in.readLine().toString();
        } catch (Exception e) {
            e.printStackTrace();
            inputLine = "";
        }

        return inputLine;
    }

    public static Map<String, String> configBuilder(String subject, String body, String price, String tradeNo, String type) {

        Map<String, String> info = Maps.newHashMap();
        // 签约合作者身份ID
        info.put("partner",PARENT_ID);
        // 签约卖家支付宝账号
        info.put("seller_id",PARENT_ID);
        // 商户网站唯一订单号
        info.put("out_trade_no", tradeNo);
        // 商品名称
        info.put("subject", subject);
        // 商品详情
        info.put("body", body);
        // 商品金额
        info.put("total_fee", price);
        // 服务器异步通知页面路径
        info.put("notify_url",NOTIFY_URL);

        // 支付类型， 固定值
        info.put("payment_type", "1");
        // 参数编码， 固定值
        info.put("_input_charset", "utf-8");
        // 服务接口名称， 固定值
        info.put("service", "mobile.securitypay.pay");
        // 设置未付款交易的超时时间
        // 默认30分钟，一旦超时，该笔交易就会自动被关闭。
        // 取值范围：1m～15d。
        // m-分钟，h-小时，d-天，1c-当天（无论交易何时创建，都在0点关闭）。
        // 该参数数值不接受小数点，如1.5h，可转换为90m。
        info.put("it_b_pay", "30m");
        // 调用银行卡支付，需配置此参数，参与签名， 固定值 （需要签约《无线银行卡快捷支付》才能使用）
//		info.put("paymethod", "expressGateway");
        if (type.equals("web")) {
            // 支付宝处理完请求后，当前页面跳转到商户指定页面的路径，可空
            info.put("return_url", RETURN_URL);
        }

        return info;
    }

    /**
     * sign the order info. 对订单信息进行签名
     *
     * @param content 待签名订单信息
     */
    public static String sign(String content) {
        return RSA.sign(content, RSA_PRIVATE,input_charset);
    }


    public static void main(String[] args) {
        String content = "_input_charset=\"utf-8\"&body=\"支付宝充值0.01元\"&it_b_pay=\"30m\"&notify_url=\"http://test.lanbaoo.com/growth/alipay/async\"&out_trade_no=\"C2015061817543146686\"&partner=\"2088011813163600\"&payment_type=\"1\"&seller_id=\"2088011813163600\"&service=\"mobile.securitypay.pay\"&subject=\"用户充值\"&total_fee=\"0.01\"";
        try {
            System.out.println(URLEncoder.encode(AlipayUtil.sign(content), "UTF-8"));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}